package webssh

import (
	"bytes"
	"context"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

type WebSSHConfig struct {
	Record     bool
	RecPath    string
	RemoteAddr string
	User       string
	Password   string
	AuthModel  AuthModel
	PkPath     string
}

type WebSSH struct {
	*WebSSHConfig
}

func NewWebSSH(conf *WebSSHConfig) *WebSSH {
	return &WebSSH{
		WebSSHConfig: conf,
	}
}

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024 * 10,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

// func DecodedMsgToSSHClient(msg string) (SSHClient, error) {
// 	client := NewSSHClient()
// 	decoded, err := base64.StdEncoding.DecodeString(msg)
// 	if err != nil {
// 		return client, err
// 	}
// 	err = json.Unmarshal(decoded, &client)
// 	if err != nil {
// 		return client, err
// 	}
// 	return client, nil
// }

// func (w WebSSH) ServeConn(c *gin.Context) {
// 	// 获取 IP， 端口， 用户名，密码
// 	// c.Params("")
// 	// username := c.DefaultQuery("username", "")
// 	username := c.Query("username")
// 	password := c.Query("password")
// 	msg := c.Query("msg")
// 	fmt.Printf("username:password [%v:%v]\n", username, password)
// 	w.WebSSHConfig.User = username
// 	w.WebSSHConfig.Password = password
// 	// w.User = username
// 	// w.Password = password
// 	nodeJson, err := base64.StdEncoding.DecodeString(msg)
// 	if err != nil {
// 		panic(err)
// 	}
// 	fmt.Printf("decoded: %s\n", nodeJson)

// 	node := Node{}
// 	err = json.Unmarshal(nodeJson, &node)
// 	fmt.Printf("node: %+v\n", node)

// 	// fmt.Printf("webssh: %+v\n", &webssh)
// 	fmt.Printf("w.WebSSHConfig.User: %v\n", w.WebSSHConfig.User)
// 	fmt.Printf("w.WebSSHConfig.Password: %v\n", w.WebSSHConfig.Password)
// 	// if err != nil {
// 	// 	panic(err)
// 	// }

// 	wsConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
// 	if err != nil {
// 		c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": err.Error()})
// 		return
// 	}
// 	defer wsConn.Close()
// 	var config *SSHClientConfig

// 	// fmt.Printf("config.User: %v\n", config.User)
// 	// fmt.Printf("config.Password: %v\n", config.Password)
// 	switch w.AuthModel {

// 	case PASSWORD:
// 		config = SSHClientConfigPassword(
// 			w.RemoteAddr,
// 			w.User,
// 			w.Password,
// 		)
// 	case PUBLICKEY:
// 		config = SSHClientConfigPulicKey(
// 			w.RemoteAddr,
// 			w.User,
// 			w.PkPath,
// 		)
// 	}

// 	client, err := NewSSHClient(config)
// 	if err != nil {
// 		wsConn.WriteControl(websocket.CloseMessage,
// 			[]byte(err.Error()), time.Now().Add(time.Second))
// 		return
// 	}
// 	defer client.Close()

// 	var recorder *Recorder
// 	if w.Record {
// 		// mask := syscall.Umask(0)
// 		// defer syscall.Umask(mask)
// 		os.MkdirAll(w.RecPath, 0766)
// 		fileName := path.Join(w.RecPath, fmt.Sprintf("%s_%s_%s.cast", w.RemoteAddr, w.User, time.Now().Format("20060102_150405")))
// 		f, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0766)
// 		if err != nil {
// 			c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": err.Error()})
// 		}
// 		defer f.Close()
// 		recorder = NewRecorder(f)
// 	}

// 	turn, err := NewTurn(wsConn, client, recorder)

// 	if err != nil {
// 		wsConn.WriteControl(websocket.CloseMessage,
// 			[]byte(err.Error()), time.Now().Add(time.Second))
// 		return
// 	}
// 	defer turn.Close()

// 	var logBuff = bufPool.Get().(*bytes.Buffer)
// 	logBuff.Reset()
// 	defer bufPool.Put(logBuff)

// 	ctx, cancel := context.WithCancel(context.Background())
// 	wg := sync.WaitGroup{}
// 	wg.Add(2)
// 	go func() {
// 		defer wg.Done()
// 		err := turn.LoopRead(logBuff, ctx)
// 		if err != nil {
// 			log.Printf("%#v", err)
// 		}
// 	}()
// 	go func() {
// 		defer wg.Done()
// 		err := turn.SessionWait()
// 		if err != nil {
// 			log.Printf("%#v", err)
// 		}
// 		cancel()
// 	}()
// 	wg.Wait()
// }

func ServerWs(c *gin.Context) {
	// 获取 IP， 端口， 用户名，密码
	// c.Params("")
	// username := c.DefaultQuery("username", "")
	confing := &WebSSHConfig{
		Record:  true,
		RecPath: "./rec/",
		// RemoteAddr: "localhost:22",
		// RemoteAddr: "192.168.153.138:22",
		// User:       "root",
		// Password:   "mmmmmm",
		AuthModel: PASSWORD,
	}

	username := c.Query("username")
	password := c.Query("password")
	// ip := c.Query("ip")
	// port := c.Query("port")
	msg := c.Query("msg")
	fmt.Printf("username:password [%v:%v]\n", username, password)
	// w.WebSSHConfig.User = username
	// w.WebSSHConfig.Password = password
	// confing.User = username
	// confing.Password = password
	// confing.RemoteAddr = ip + "" + port

	webssh := NewWebSSH(confing)
	// w.User = username
	// w.Password = password
	nodeJson, err := base64.StdEncoding.DecodeString(msg)
	if err != nil {
		panic(err)
	}
	fmt.Printf("decoded: %s\n", nodeJson)

	node := Node{}
	err = json.Unmarshal(nodeJson, &node)

	fmt.Printf("node: %+v\n", node)
	confing.RemoteAddr = node.IP + ":" + strconv.Itoa(int(node.Port))
	confing.User = node.Username
	confing.Password = node.Password
	fmt.Printf("confing: %+v\n", confing)
	// fmt.Printf("webssh: %+v\n", &webssh)
	// fmt.Printf("w.WebSSHConfig.User: %v\n", w.WebSSHConfig.User)
	// fmt.Printf("w.WebSSHConfig.Password: %v\n", w.WebSSHConfig.Password)
	// if err != nil {
	// 	panic(err)
	// }

	wsConn, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": err.Error()})
		return
	}
	defer wsConn.Close()
	var config *SSHClientConfig

	// fmt.Printf("config.User: %v\n", config.User)
	// fmt.Printf("config.Password: %v\n", config.Password)
	switch webssh.AuthModel {

	case PASSWORD:
		config = SSHClientConfigPassword(
			webssh.RemoteAddr,
			webssh.User,
			webssh.Password,
		)
	case PUBLICKEY:
		config = SSHClientConfigPulicKey(
			webssh.RemoteAddr,
			webssh.User,
			webssh.PkPath,
		)
	}

	client, err := NewSSHClient(config)
	if err != nil {
		wsConn.WriteControl(websocket.CloseMessage,
			[]byte(err.Error()), time.Now().Add(time.Second))
		return
	}
	defer client.Close()

	var recorder *Recorder
	if webssh.Record {
		// mask := syscall.Umask(0)
		// defer syscall.Umask(mask)
		os.MkdirAll(webssh.RecPath, 0766)
		fileName := path.Join(webssh.RecPath, fmt.Sprintf("%s_%s_%s_from-%s.cast", webssh.RemoteAddr, webssh.User, time.Now().Format("2006-01-02_15-04-05"), c.ClientIP()))
		f, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0766)
		if err != nil {
			c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": err.Error()})
		}
		defer f.Close()
		recorder = NewRecorder(f)
	}

	turn, err := NewTurn(wsConn, client, recorder)

	if err != nil {
		wsConn.WriteControl(websocket.CloseMessage,
			[]byte(err.Error()), time.Now().Add(time.Second))
		return
	}
	defer turn.Close()

	var logBuff = bufPool.Get().(*bytes.Buffer)
	logBuff.Reset()
	defer bufPool.Put(logBuff)

	ctx, cancel := context.WithCancel(context.Background())
	wg := sync.WaitGroup{}
	wg.Add(2)
	go func() {
		defer wg.Done()
		err := turn.LoopRead(logBuff, ctx)
		if err != nil {
			log.Printf("%#v", err)
		}
	}()
	go func() {
		defer wg.Done()
		err := turn.SessionWait()
		if err != nil {
			log.Printf("%#v", err)
		}
		cancel()
	}()
	wg.Wait()
}

func RecoderPlay(c *gin.Context) {
	fmt.Println("INTO RecoderPlay")
	files, err := ioutil.ReadDir("./rec/")
	if err != nil {
		c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": err.Error()})
		return
	}
	var filesName []string
	for _, f := range files {
		if f.IsDir() {
			continue
		}

		if strings.HasSuffix(f.Name(), ".cast") {
			filesName = append(filesName, f.Name())
		}
	}
	c.JSON(200, filesName)
}

// func (w WebSSH) RecoderList(c *gin.Context) {
// 	files, err := ioutil.ReadDir(w.RecPath)
// 	if err != nil {
// 		c.AbortWithStatusJSON(200, gin.H{"ok": false, "msg": err.Error()})
// 		return
// 	}
// 	var filesName []string
// 	for _, f := range files {
// 		if f.IsDir() {
// 			continue
// 		}

// 		if strings.HasSuffix(f.Name(), ".cast") {
// 			filesName = append(filesName, f.Name())
// 		}
// 	}
// 	c.JSON(200, filesName)
// }
